package org.deltaset.faces.module;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewDeclarationLanguageFactory;

/**
 * To register this ViewHandler, add the following to your faces-config.xml
 * also check if org.deltaset.common.faces.module.ClasspathResourcerResolver is properly registered
 * <application>
 *         <view-handler>org.deltaset.common.faces.module.DeltasetViewHandler</view-handler>
 * </application>
 * 
 * @author Dimitar Makariev
 */
public class DeltasetViewHandler extends ViewHandler {

    private final static String CN = DeltasetViewHandler.class.getName();
    private final ViewHandler defaultViewHandler;
    private final ViewDeclarationLanguageFactory vdlFactory;
    private static final Logger logger = Logger.getLogger(DeltasetViewHandler.class.getName());

    public DeltasetViewHandler(ViewHandler defaultViewHandler) {
        this.defaultViewHandler = defaultViewHandler;
        this.vdlFactory = (ViewDeclarationLanguageFactory) FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
    }

    @Override
    public String deriveViewId(final FacesContext context, final String rawViewIdParam) {

        final String rawViewId = remapToXhtml(rawViewIdParam);

        final String deriveViewId = defaultViewHandler.deriveViewId(context, rawViewId);

        try {
            final String classpathViewId = Thread.currentThread().getContextClassLoader().getResource("META-INF" + rawViewId).getFile();
            if (null == deriveViewId && classpathViewId != null) {
                return rawViewId;
            }

        } catch (Exception e) {
            logger.log(Level.FINEST, " deriveViewId e:{0} rawViewId={1}", new Object[]{e.toString(), rawViewId});           
        }

        return deriveViewId;
    }

    private static String remapToXhtml(String viewId) {
        if (null != viewId && !viewId.endsWith(".xhtml")) {
            int ind = viewId.lastIndexOf('.');
            return (ind > 0) ? viewId.substring(0, ind) + ".xhtml" : viewId;
        }
        return viewId;
    }

    @Override
    public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId) {
        if (null == context) {
            logger.log(Level.SEVERE, ".getViewDeclarationLanguage FacesContext cannot be null !");
            throw new IllegalArgumentException(CN + ".getViewDeclarationLanguage FacesContext cannot be null !");
        }
        final String actualViewId = this.deriveViewId(context, viewId);
        return vdlFactory.getViewDeclarationLanguage(actualViewId);
    }

    @Override
    public UIViewRoot restoreView(FacesContext context, String viewId) {
        if (null == context) {
            logger.log(Level.SEVERE, ".restoreView FacesContext cannot be null !");
            throw new IllegalArgumentException(CN + ".restoreView FacesContext cannot be null !");
        }
        final String actualViewId = this.deriveViewId(context, viewId);
        return vdlFactory.getViewDeclarationLanguage(actualViewId).restoreView(context, actualViewId);
    }

    @Override
    public UIViewRoot createView(FacesContext context, String viewId) {
        if (null == context) {
            logger.log(Level.SEVERE, ".createView FacesContext cannot be null !");
            throw new IllegalArgumentException(CN + ".createView FacesContext cannot be null !");
        }
        final String actualViewId = this.deriveViewId(context, viewId);
        return vdlFactory.getViewDeclarationLanguage(actualViewId).createView(context, actualViewId);
    }

    @Override
    public String getBookmarkableURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) {
        return defaultViewHandler.getBookmarkableURL(context, viewId, parameters, includeViewParams);
    }

    @Override
    public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) {
        return defaultViewHandler.getRedirectURL(context, viewId, parameters, includeViewParams);
    }

    @Override
    public Locale calculateLocale(FacesContext context) {
        return defaultViewHandler.calculateLocale(context);
    }

    @Override
    public String calculateRenderKitId(FacesContext context) {
        return defaultViewHandler.calculateRenderKitId(context);
    }

    @Override
    public String getActionURL(FacesContext context, String viewId) {
        return defaultViewHandler.getActionURL(context, viewId);
    }

    @Override
    public String getResourceURL(FacesContext context, String path) {
        return defaultViewHandler.getResourceURL(context, path);
    }

    @Override
    public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {
        defaultViewHandler.renderView(context, viewToRender);
    }

    @Override
    public void writeState(FacesContext context) throws IOException {
        defaultViewHandler.writeState(context);
    }

    @Override
    public void initView(FacesContext context) throws FacesException {
        defaultViewHandler.initView(context);
    }

    @Override
    public String calculateCharacterEncoding(FacesContext context) {
        return defaultViewHandler.calculateCharacterEncoding(context);
    }
}
